with Ada.Integer_Text_IO;	use Ada.Integer_Text_IO;

with Ada.Text_IO;			use Ada.Text_IO;

with Ada.Command_Line;		use Ada.Command_Line;

with Ada.Strings;			use Ada.Strings;

with Ada.Strings.Unbounded;		use Ada.Strings.Unbounded;

with Ada.Characters.Handling;	use Ada.Characters.Handling;

-- character -> ascii value using Character'Pos
-- ascii value -> character using Character'Val
procedure main is
	function encrypt(b : in Character; o : in Character) return Character is
		output	: Integer;
		base	: Integer;
		offset	: Integer;
	begin
		base   := Character'pos(b);
		offset := (Character'pos(o) - 97);
		output := (base + offset);
		if output > 122 then
			output := (output mod 123) + 97;
		end if;
		return Character'val(output);
	end encrypt;

	function decrypt(b : in Character; o : in Character) return Character is
		output	: Integer;
		base	: Integer;
		offset	: Integer;
	begin
		base   := Character'pos(b);
		offset := (Character'pos(o) - 97);
		output := (base - offset);
		if output < 97 then
			output := 123  - (97 - output);
		end if;
		return Character'val(output);
	end decrypt;

	-- when the string 'k' is too short, expand it to a length of 'n'
	function adjustkeypass(
		k : in Unbounded_String; 
		n : in Integer) 
		return Unbounded_String is
	output		: Unbounded_String;
	iterations	: Integer;
	index		: Integer;
	begin
		output		:= k;
		iterations	:= n - length(k);
		for i in Integer range 1 .. iterations loop
			-- unbounded strings are 1-indexed.
			if i > length(k) then
				index := index mod length(k) + 1;
			else 
				index := i;
			end if;
			append(output, element(k, index));
		end loop;
		return output;
	end adjustkeypass;

	function vigeneres(
		p : in Unbounded_String; 
		k : in out Unbounded_String;
		is_encrypt : in Boolean)
		return Unbounded_String  is
	output	: Unbounded_String;
	newchar : Character range 'a' .. 'z';
	begin
		-- keypass needs to be truncated or expanded to match phrase's length
		output := p;
		if length(k) < length(p) then
			k := adjustkeypass(k, length(p));
		end if;
		for i in Integer range 1 .. length(p) loop
			if is_letter(element(k, i)) then
				if is_encrypt then
					newchar := encrypt(to_lower(element(p, i)), 
									   to_lower(element(k, i)));
				else
					newchar := decrypt(to_lower(element(p, i)), 
									   to_lower(element(k, i)));
				end if;
				overwrite(output, i, newchar & "");
			end if;
		end loop; -- for loop
		return output;
	end vigeneres;

phrase	: Unbounded_String;
keypass	: Unbounded_String;
result  : Unbounded_String;
begin
	if Argument_Count < 2 then
		Put_Line("usage: <program> <phrase> <passphrase>");
		return;
	end if;

	phrase := To_Unbounded_String(Argument(1));
	keypass := To_Unbounded_String(Argument(2));
	result := vigeneres(phrase, keypass, true);
	put_line(to_string(result));
	result := vigeneres(result, keypass, false);
	put_line(to_string(result));
	if result = phrase then
		put_line("Vigenere's basic cipher is working.");
	end if;
end main;
